package net.ufrog.common.utils;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageConfig;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import net.ufrog.common.CoreConfig;
import net.ufrog.common.Logger;
import net.ufrog.common.exception.ServiceException;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

/**
 * 二维码工具类
 * 
 * @author ultrafrog
 * @version 1.0, 2015-10-29
 * @since 1.0
 */
public class QRs {

    /** 构造函数 */
    private QRs() {}

    /**
     * 编码比特矩阵
     *
     * @param content 内容
     * @param width 宽度
     * @param height 高度
     * @return 比特矩阵
     */
    public static BitMatrix encode(String content, Integer width, Integer height) {
        try {
            Map<EncodeHintType, Object> hints = new HashMap<>();
            hints.put(EncodeHintType.CHARACTER_SET, CoreConfig.getCharset().name());
            hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
            hints.put(EncodeHintType.MARGIN, CoreConfig.getQrMargin());
            return new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
        } catch (WriterException e) {
            Logger.error(e.getMessage(), e);
            throw new ServiceException(e.getMessage(), e);
        }
    }

    /**
     * 转换成图像缓冲
     *
     * @param content 内容
     * @param width 宽度
     * @param height 高度
     * @return 图像缓存
     */
    public static BufferedImage toBufferedImage(String content, Integer width, Integer height) {
        return MatrixToImageWriter.toBufferedImage(encode(content, width, height), new MatrixToImageConfig(0xFF000001, 0xFFFFFFFF));
    }

    /**
     * 转换成图像缓冲<br>
     * 中央添加图标
     *
     * @param content 内容
     * @param width 宽度
     * @param height 高度
     * @param logo 图标
     * @return 图像缓存
     */
    public static BufferedImage toBufferedImage(String content, Integer width, Integer height, Logo logo) {
        try {
            BufferedImage qrImg = toBufferedImage(content, width, height);
            Graphics2D graphics = qrImg.createGraphics();
            Integer logoWidth = qrImg.getWidth() / logo.getScale();
            Integer logoHeight = qrImg.getHeight() / logo.getScale();
            Integer offsetX = (qrImg.getWidth() - logoWidth) / 2;
            Integer offsetY = (qrImg.getHeight() - logoHeight) / 2;

            Path resizeLogo = Paths.get(Codecs.uuid());
            Images.resize(logo.getPath().toFile(), resizeLogo.toFile(), logoWidth, logoHeight, false);
            BufferedImage logoImg = ImageIO.read(resizeLogo.toFile());

            graphics.drawImage(logoImg, offsetX, offsetY, null);
            graphics.drawRoundRect(offsetX, offsetY, logoWidth, logoHeight, 15, 15);
            graphics.setStroke(new BasicStroke(logo.getBorder()));
            graphics.setColor(logo.getBorderColor());
            graphics.drawRect(offsetX, offsetY, logoWidth, logoHeight);
            graphics.dispose();
            java.nio.file.Files.delete(resizeLogo);

            return qrImg;
        } catch (IOException e) {
            Logger.error(e.getMessage(), e);
            throw new ServiceException(e.getMessage(), e);
        }
    }

    /**
     * 输出到文件
     *
     * @param content 内容
     * @param width 宽度
     * @param height 高度
     * @param format 格式
     * @param path 目标文件路径
     */
    public static void writeToPath(String content, Integer width, Integer height, String format, Path path) {
        try {
            MatrixToImageWriter.writeToPath(encode(content, width, height), format, path);
        } catch (IOException e) {
            Logger.error(e.getMessage(), e);
            throw new ServiceException(e.getMessage(), e);
        }
    }

    /**
     * 输出带图片<br>
     * 中央添加图标
     *
     * @param content 内容
     * @param width 宽度
     * @param height 高度
     * @param format 格式
     * @param path 目标文件地址
     * @param logo 图标
     */
    public static void writeToPath(String content, Integer width, Integer height, String format, Path path, Logo logo) {
        try {
            ImageIO.write(toBufferedImage(content, width, height, logo), format, path.toFile());
        } catch (IOException e) {
            Logger.error(e.getMessage(), e);
            throw new ServiceException(e.getMessage(), e);
        }
    }

    /**
     * 输出到流
     *
     * @param content 内容
     * @param width 宽度
     * @param height 高度
     * @param format 格式
     * @param stream 输出流
     */
    public static void writeToStream(String content, Integer width, Integer height, String format, OutputStream stream) {
        try {
            MatrixToImageWriter.writeToStream(encode(content, width, height), format, stream);
        } catch (IOException e) {
            Logger.error(e.getMessage(), e);
            throw new ServiceException(e.getMessage(), e);
        }
    }

    /**
     * 输出到流<br>
     * 中央添加图标
     *
     * @param content 内容
     * @param width 宽度
     * @param height 高度
     * @param format 格式
     * @param stream 输出流
     * @param logo 图标
     */
    public static void writeToStream(String content, Integer width, Integer height, String format, OutputStream stream, Logo logo) {
        try {
            ImageIO.write(toBufferedImage(content, width, height, logo), format, stream);
        } catch (IOException e) {
            Logger.error(e.getMessage(), e);
            throw new ServiceException(e.getMessage(), e);
        }
    }

    /**
     * 图标
     *
     * @author ultrafrog
     * @version 1.0, 2015-10-30
     * @since 1.0
     */
    public static class Logo {

        public static final Integer DEFAULT_SCALE		= 5;
        public static final Integer DEFAULT_BORDER		= 2;
        public static final Color DEFAULT_BORDER_COLOR	= Color.WHITE;

        /** 图片路径 */
        private final Path path;

        /** 图片比例 */
        private final Integer scale;

        /** 边框宽度 */
        private final Integer border;

        /** 边框颜色 */
        private final Color borderColor;

        /**
         * 构造函数
         *
         * @param path 图片路径
         */
        public Logo(String path) {
            this(path, DEFAULT_SCALE, DEFAULT_BORDER, DEFAULT_BORDER_COLOR);
        }

        /**
         * 构造函数
         *
         * @param path 图片路径
         */
        public Logo(Path path) {
            this(path, DEFAULT_SCALE, DEFAULT_BORDER, DEFAULT_BORDER_COLOR);
        }

        /**
         * 构造函数
         *
         * @param path 图片路径
         * @param border 边框宽度
         * @param borderColor 边框颜色
         */
        public Logo(String path, Integer scale, Integer border, Color borderColor) {
            this(Paths.get(path), scale, border, borderColor);
        }

        /**
         * 构造函数
         *
         * @param path 图片路径
         * @param scale 图片比例
         * @param border 边框宽度
         * @param borderColor 边框颜色
         */
        public Logo(Path path, Integer scale, Integer border, Color borderColor) {
            this.path = path;
            this.scale = scale;
            this.border = border;
            this.borderColor = borderColor;
        }

        /**
         * 读取图片路径
         *
         * @return 图片路径
         * @see #path
         */
        public Path getPath() {
            return path;
        }

        /**
         * 读取图片比例
         *
         * @return 图片比例
         * @see #scale
         */
        public Integer getScale() {
            return scale;
        }

        /**
         * 读取边框宽度
         *
         * @return 边框宽度
         * @see #border
         */
        public Integer getBorder() {
            return border;
        }

        /**
         * 读取边框颜色
         *
         * @return 边框颜色
         * @see #borderColor
         */
        public Color getBorderColor() {
            return borderColor;
        }
    }
}
